package com.javawork.juc.three;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
 * LockSupport是一个践程阻塞工具类，所有的方法都是静态方法，可以让线程在任意位置阻塞，阻塞之后也有对应的唤醒方法。归根结底，LockSupport调用的Unsafe中的native代码。
 * LockSupport 提供park（）和unpark（）方法实现阻塞线程和解除线程阻塞的过程LockSupport和每个使用它的线程都有一个许可（permit）关联。
 * 每个线程都有一个相关的permit，permit最多只有一个，重复调用unpark也不会积累凭证。
 * 形象的理解
 * 线程阻塞需要消耗凭证（permit），这个凭证最多只有1个。
 * 当调用park方法时
 * *如果有凭证，则会直接消耗掉这个凭证然后正常退出；
 * *如果无凭证，就必须阻塞等待凭证可用；而unpark则相反，它会增加一个凭证，但凭证最多只能有1个，累加无效。
 * <br/>
 * 为什么可以突破wait/notify的原有调用顺序？
 * 因为unpark获得了一个凭证，之后再调用park方法，就可以名正言顺的凭证消费，故不会阻塞。
 * 先发放了凭证后续可以畅通无阻、为什么唤醒两次后阻塞两次，但最终结果还会阻塞线程？
 * 因为凭证的数量最多为1，连续调用两次unpark和调用一次unpark效果一样，只会增加一个凭证；而调用两次park却需要消费两个凭证，证不够，不能放行。
 * @author zhangxiaolu
 * @since 2023-02-09 22:40
 */
public class LockSupportParkAndUnparkDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "---come in");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "------被唤醒");
        }, "t1");
        t1.start();

//        try {
//            TimeUnit.SECONDS.sleep(1);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        new Thread(() -> {
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName() + "------发出唤醒");
        }, "t2").start();
    }
}
